from __future__ import with_statement

#
#  DBDocument.py
#  DrawingBoard
#
#  Created by Dethe Elza on 22/02/05.
#  Copyright (c) 2005 Living Code Enterprises. All rights reserved.
#


from AppKit import *
from Foundation import *
from PyObjCTools import NibClassBuilder
from DBCanvasView import DBCanvasView
from DBInklingDrawerWindow import DBInklingDrawerWindow, DBInklingDrawer
from DBMovieMaker import DBMovieMaker
import NSImageExt

class DBDocument(NibClassBuilder.AutoBaseClass):
    # the actual base class is NSDocument
    # outlets:
    # documentWindow
    # canvas
    # frameCounter
    
    DEFAULT_PEN_COLOR = NSColor.blackColor()
    DEFAULT_PEN_WIDTH = 4
        
    path = None
    fileContents = None
    _frameCounter = None
    _penWidthSlider = None
    _colorWell = None
    _pencolor = DEFAULT_PEN_COLOR
    _penwidth = DEFAULT_PEN_WIDTH
    _scalingFactor = 1.0
    _window = None
    
    def awakeFromNib(self):
        self.documentWindow.setFrame_display_(self.documentWindow.screen().visibleFrame(), False)
        self.initCanvas()
        self.updateFrameCounter()
        self.documentWindow.makeKeyAndOrderFront_(self)
            
    def windowNibName(self):
        return "DBDocument"
        
    def initCanvas(self):
        #print 'DBDocument.initCanvas called'
        self.canvas.setDocument_(self)
        self.canvas.setMaxSize()
        if self.fileContents:
            #print 'fileContents: %s' % self.fileContents
            #self.canvas.loadData_(self.data)
            self.canvas.setData_(self.fileContents)
        self.updateFrameCounter()
        self.canvas.setCursor()
        
    def setPenWidthSlider_(self, slider):
        self._penWidthSlider = slider
        slider.setIntValue_(self._penwidth)
        
    def setColorWell_(self, colorwell):
        self._colorWell = colorwell
        colorwell.setColor_(self._pencolor)
        
    def updateFrameCounter(self):
        self.frameCounter.setStringValue_(self.canvas.frameCount())
                
    def dataRepresentationOfType_(self, type):
        #print 'dataRepresentationOfType_(%s)' % type
        data = '\n'.join([x for x in self.canvas.animationData()])
        self.updateChangeCount_(NSChangeCleared) # This should be in a notification method
        return NSData.alloc().initWithBytes_length_(data,len(data))
        
    def loadDataRepresentation_ofType_(self, data, type):
        #print 'loadDataRepresentation_ofType_(data, %s)' % type
        #self.fileContents = data.copyWithZone_(self.zone())
        self.fileContents = str(data).splitlines()
        return True

    def windowControllerDidLoadNib_(self, controller):
        #print 'windowControllerDidLoadNib_(%s)' % controller
        # if data was loaded from file, can now update window
        if self.fileContents:
            self._pencolor = self.canvas.lastPenColor() or self.DEFAULT_PEN_COLOR
            if self._colorWell:
                self._colorWell.setColor_(self._pencolor)
            self._penwidth = self.canvas.lastPenWidth() or self.DEFAULT_PEN_WIDTH
            if self._penWidthSlider:
                self._penWidthSlider.setIntValue_(self._penwidth)
            self.canvas.setCursor()
            self.updateFrameCounter()
    
    def validateUserInterfaceItem_(self, item):
        title = item.title()
        if title == 'Undo Remove Frame':
            return self.canvas.canUndoRemoveFrame()
        elif title == 'Next Frame':
            return self.canvas.hasNextFrame()
        elif title == 'Previous frame':
            return self.canvas.hasPreviousFrame()
        elif title == 'Undo Line':
            return self.canvas.canUndoLine()
        elif title == 'Redo Line':
            return self.canvas.canRedoLine()
        else:
            return super(DBDocument, self).validateUserInterfaceItem_(item)
        
#======================================================================
#
#    The following methods are menu handlers which apply to the document
#
#======================================================================

    def addFrame_(self, sender=None):
        #print 'document.addFrame()'
        self.canvas.addFrame()
        self.updateFrameCounter()
        
    def addFrameFromCurrentFrame_(self, sender):
        self.canvas.addFrameFromCurrentFrame()
        self.updateFrameCounter()
        
    def removeFrame_(self, sender):
        self.canvas.removeFrame()
        self.updateFrameCounter()
        
    def firstFrame_(self, sender):
        self.canvas.firstFrame()
        self.updateFrameCounter()

    def lastFrame_(self, sender):
        self.canvas.lastFrame()
        self.updateFrameCounter()

    def nextFrame_(self, sender):
        #print 'document.nextFrame()'
        self.canvas.nextFrame()
        self.updateFrameCounter()

    def play_(self, sender):
        #print 'document.play()'
        self.canvas.play_(sender)

    def previousFrame_(self, sender):
        #print 'document.previousFrame()'
        self.canvas.previousFrame()
        self.updateFrameCounter()

    def changeColor_(self, sender):
        #print 'document.setColor()'
        self._pencolor = sender.color()
        self.canvas.setCursor()
        
    def pencolor(self):
        return self._pencolor
        
    def selectEraser_(self, sender):
        self.canvas.setPenColor_(NSColor.clearColor())
        self._colorWell.setColor_(NSColor.whiteColor())
        
    def setLineWidth_(self, sender):
        #print 'document.setLineWidth()'
        if sender is self._penWidthSlider:
            self._penwidth = sender.intValue()
        else: # restoring from saved value, sender is actual value
            self._penwidth = sender
            self._penWidthSlider.setIntValue_(sender)
        self.canvas.setCursor()
    
    def penwidth(self):
        return self._penwidth
        
    def setTransparency_(self, menuitem):
        title = menuitem.title()
        if title == u'Fully Opaque':
            self.window().setAlphaValue_(1.0)
        elif title == u'25% Transparent':
            self.window().setAlphaValue_(0.75)
        elif title == u'50% Transparent':
            self.window().setAlphaValue_(0.50)
        elif title == u'75% Transparent':
            self.window().setAlphaValue_(0.25)
        elif title == u'Fully Transparent':
            self.window().setAlphaValue_(0.0)
        #print 'window transparency set to %s' % self.window().alphaValue()
        
    def exportMovieAsSVG_(self, sender):
        savePanel = NSSavePanel.savePanel()
        savePanel.setRequiredFileType_(u'svg')
        ok_cancel = savePanel.runModal()
        if ok_cancel == NSFileHandlingPanelOKButton:
            filename = savePanel.filename()
            f = file(filename, 'w')
            f.write(self.canvas.animationDataAsSVG())
            f.close()
            
    def exportMovieAsPNGSequence_(self, sender):
        self._exportMovieAsImages_(u'png')
            
    def _exportMovieAsImages_(self, filetype):
        import os
        filepath = self._getSaveFilenameForFiletype_(filetype)
        if filepath is None:
            return
        # create directory
        dirpath, extension = os.path.splitext(filepath)
        os.mkdir(dirpath)
        discard, basepath = os.path.split(dirpath)
        magnitude = len(str(len(self.canvas.frames) + 1)) # how many digits needed to count frames from 1?
        format_string = '%%s_%%0%sd%%s' % magnitude # build the format string
        bounds = self.canvas.drawnBounds()
        for idx, image in enumerate(self.canvas.frameImages()):
            filename = os.path.join(dirpath, format_string % (basepath, idx + 1, extension))
            self._saveFrameImage_withBounds_toFilename_(image, bounds, filename)
            
    def exportMovieAsQuicktime_(self, sender):
        savePanel = NSSavePanel.savePanel()
        savePanel.setRequiredFileType_(u'mov')
        ok_cancel = savePanel.runModal()
        if ok_cancel == NSFileHandlingPanelOKButton:
            filename = savePanel.filename()
            movie_maker = DBMovieMaker(filename, self.canvas._quicktimeTimer)
            movie_maker.addImages(self.canvas.frameImages())
            movie_maker.saveMovie()

    def _getSaveFilenameForFiletype_(self, filetype):
        savePanel = NSSavePanel.savePanel()
        savePanel.setRequiredFileType_(filetype)
        ok_cancel = savePanel.runModal()
        if ok_cancel == NSFileHandlingPanelOKButton:
            return savePanel.filename()
        else:
            return None
            
    def _saveFrameImage_withBounds_toFilename_(self, image, srcrect, filename):
        # print 'DBDocument image frame bounds:', srcrect
        saveimage = NSImage.imageWithSize_(srcrect[1])
        with saveimage:
            image.compositeToPoint_fromRect_operation_fraction_(
                (0,0),
                srcrect,
                NSCompositeCopy,
                1.0)
        saveimage.writeToFilePath_(filename)
        
    
    def _exportFrameAsFiletype_(self, filetype):
        filename = self._getSaveFilenameForFiletype_(filetype)
        if filename is None: 
            return
        image = self.canvas.frameImage()
        self._saveFrameImage_withBounds_toFilename_(image, self.canvas.frameBounds(), filename)
            
    def exportFrameAsPNG_(self, sender):
        self._exportFrameAsFiletype_(u'png')
        
    def exportFrameAsGIF_(self, sender):
        self._exportFrameAsFiletype_(u'gif')
        
    def exportFrameAsJPEG_(self, sender):
        self._exportFrameAsFiletype_(u'jpeg')
        
    def undoLine_(self, sender):
        self.canvas.undoLine()
        
    def undoSegment_unimplemented_(self, sender):
        self.canvas.undoSegment()
        
    def redoLine_(self, sender):
        self.canvas.redoLine()
        
    def redoSegment_unimplemented_(self, sender):
        self.canvas.redoSegment()
        
    def undoRemoveFrame_(self, sender):
        self.canvas.undoRemoveFrame()
        
    def nudgeFrameUp_(self, sender):
        self.canvas.translateFrameXBy_yBy_(0.0, 2.0)

    def nudgeFrameUpMore_(self, sender):
        # print 'nudgeFrameUpMore'
        self.canvas.translateFrameXBy_yBy_(0.0, 10.0)

    def nudgeFrameDown_(self, sender):
        self.canvas.translateFrameXBy_yBy_(0.0, -2.0)

    def nudgeFrameDownMore_(self, sender):
        self.canvas.translateFrameXBy_yBy_(0.0, -10.0)

    def nudgeFrameLeft_(self, sender):
        self.canvas.translateFrameXBy_yBy_(-2.0, 0.0)

    def nudgeFrameLeftMore_(self, sender):
        self.canvas.translateFrameXBy_yBy_(-10.0, 0.0)

    def nudgeFrameRight_(self, sender):
        self.canvas.translateFrameXBy_yBy_(2.0, 0.0)

    def nudgeFrameRightMore_(self, sender):
        self.canvas.translateFrameXBy_yBy_(10.0, 0.0)
        
    def rotateClockwise_(self, sender):
        self.canvas.rotateByDegrees_(-1.0)
        
    def rotateCounterClockwise_(self, sender):
        self.canvas.rotateByDegrees_(1.0)
        
    def setScaling_(self, sender):
        value = sender.floatValue()
        increment = 1.1
        decrement = 1.0 / increment
        bounds = self.canvas.frameDrawnBounds()
        dX, dY = NSMidX(bounds), NSMidY(bounds)
        self.canvas.translateFrameXBy_yBy_(-dX, -dY) # translate to origin
        if value > 1.0:
            self.canvas.scaleFrameXBy_yBy_(increment, increment)
        elif value < 1.0:
            self.canvas.scaleFrameXBy_yBy_(decrement, decrement)
        self.canvas.translateFrameXBy_yBy_(dX, dY) # translate back from origin        
        sender.setFloatValue_(1.0)  
        
    def setFPS_(self, popupbutton):
        #print 'setFPS: %s' % popupbutton.titleOfSelectedItem()
        # values below are how many 600ths of a second to draw for
        legal_values =  {'30 FPS': 20,
           '25 FPS': 24,
           '20 FPS': 30,
           '15 FPS': 40,
           '10 FPS': 60,
           '5 FPS': 120,
           '1 FPS': 600,
           '5 SPF': 3000,
          }
        value = popupbutton.titleOfSelectedItem()
        self.canvas.setFps_(legal_values[value])
        #popupbutton.setTitle_(value)
        
    def showAnimationBounds_(self, menu):
        if menu.state() == NSOnState:
            menu.setState_(NSOffState)
            self.canvas.setShowsCanvasBounds_(False)
        else:
            menu.setState_(NSOnState)
            self.canvas.setShowsCanvasBounds_(True)
            
    def showFrameBounds_(self, menu):
        if menu.state() == NSOnState:
            menu.setState_(NSOffState)
            self.canvas.setShowsFrameBounds_(False)
        else:
            menu.setState_(NSOnState)
            self.canvas.setShowsFrameBounds_(True)
            
    def setMovieSize64by64_(self, menu):
        self.canvas.setPreferredFrameSize_((64,64))
        
    def setMovieSize320by200_(self, menu):
        self.canvas.setPreferredFrameSize_((320,200))
        
    def setMovieSize640by480_(self, menu):
        self.canvas.setPreferredFrameSize_((640,480))
        
    def setMovieSizeMaximum_(self, menu):
        self.canvas.setPreferredFrameSize_(self.canvas.maxSize)    
            
        
#======================================================================
#
#    Window Delegate
#
#======================================================================

#    def windowWillClose_(self, notification):
#        if self.window().isDocumentEdited():
#            
